Technical Note TN2022
The Death of typeFSSpec

目次

このテクニカルノートでは、新たに提唱された typeFileURL データフォーマットについて説明し、Mac OS X のアプリケーション間でファイルへの参照を渡すために、このデータタイプを使う方法について解説します。この新しいデータタイプは、Mac OS X 上で動作するアプリケーション間で未作成ファイルへの参照を渡すのに便利な方法を提供します。

[2001年6月7日]






FSSpec データタイプは Mac OS X でファイルに関する情報をエンコードするには適していません。最も重要な問題は、FSSpec は Mac OS X のファイルシステムで使用されるような長いファイル名や Unicode ファイル名のエンコードに対応できないことです。さらに、Mac OS X ではディレクトリ ID 番号やボリューム参照番号は、アプリケーション固有なものです。つまり、あるアプリケーションで使われたディレクトリ ID 番号やボリューム参照番号が、別のアプリケーションでは何の意味も持っていません。FSSpec レコードにはボリューム参照番号とディレクトリ ID 番号が含まれており、長いファイル名を含むだけの十分なスペースがありません。そのため、FSSpec では Mac OS X のアプリケーション間でファイルへの参照を格納したり、ファイルへの参照を渡したりするのに適したエンコーディングを提供できないのです。

FSSpec レコードの欠点の大部分は Apple イベントでエンコードした Alias レコードで補うことができます。しかし、もう 1 つ、未作成ファイルへの参照という例外があります。この場合、未作成ファイルの参照を提供するために最も適しているのは、新たに提唱された typeFileURL です。

ユーザは Mac OS X では従来より長いファイル名を使えることを期待しており、新しい Navigation Service のルーチンを使うことにより可能になりました。最も重要な問題は、NavPutFile への呼び出しが、CFString に返される長いファイル名や Unicode のファイル名の使用を可能にする NavCreatePutFileDialog/NavDialogRun 呼び出しシーケンスに置き換えられていることです。このテクニカルノートでは、この情報がどのようにしてアプリケーション間の転送や、Apple イベントの要素アプリケーション間の転送にパッケージ化できるのか説明します。



typeFileURL の定義

簡潔にいうと、typeFileURL とは UTF8 フォーマットのバイトストリームにエンコードされた Core Foundation URL のことです。アプリケーションが未作成ファイルへの参照を作成しようとするときには、このデータタイプを使うことが推奨されます。他にもこのデータタイプを推奨する理由があります。それぞれの処理要件に応じて、このデータタイプはさまざまな状況で使うことが可能です。ここで typeFileURL データフォーマットのプロパティと特徴をいくつか紹介します。

  • typeAlias と typeFSRef には決定的参照が必要で、未作成ファイルを参照することはできません。typeFileURL は URLが提供する弱い「名前による」参照スタイルを使っているので、未作成ファイルへの参照がとても簡単になります。
  • typeFileURL は、'/'、':'、および Unicode 文字を含むディレクトリ名やファイル名の中で特殊文字をエンコーディングする機能を提供します
  • これは 'furl' Drag Flavor と同じデータフォーマットです。このフレーバーは HFS フレーバーを含むドラッグ参照にアタッチされています。Drag Manager によって次のように、内部的に使われます。
    1. 送信側アプリケーションにおいて:ドラッグ項目に HFS フレーバーが加えられると、Drag Manager は同じファイルを参照しているドラッグ項目の 'furl' フレーバーをエンコードします。
    2. 受信側アプリケーションにおいて:'furl' フレーバーはデコードされ、受信側アプリケーションに送られる前に、HFS フレーバー内の FSSpec レコードのフィールドを更新するために使われます。
    これで結果的に、アプリケーションはボリューム参照番号の古いディレクトリ ID 番号について心配することなく、直接 HFS フレーバーを使えるようになります。しかし同じように、typeFileURL データタイプを使うアプリケーションも、HFS フレーバーを使うことなく、エンコードされた 'furl' フレーバーを直接使うことができます。
  • Core Foundation によって提供される URL はマウントポイント情報をエンコードします。そのため、typeFileURL 参照は同じ名前のボリュームを区別することができます。
  • typeFileURL はボリューム参照番号やディレクトリ ID 番号といったプロセス固有の情報をエンコードしません。そのため複数のプロセス間でこのフォーマットをやりとりすることは有効です。
  • typeFileURL はスタティックで持続性のない参照です。つまり、あるファイルを参照するように typeFileURL が作成されても、あとでそのファイルが移動すると、その typeFileURL はもうそのファイルを参照しません。このような機能を必要とするときは、typeAlias フォーマットを使うとよいでしょう。

リスト 1 に示すルーチンでは、typeFileURL フォーマットの機能的な定義を示しています。このルーチンを使って、typeFileURL データを含む Apple イベントディスクリプタレコードを Core Foundation URL に変換することができます。Core Foundation URL 自体、複数のアプリケーションが使用できるファイルへの明確な参照を提供できます。


/* エンコード -> AEDesc
FURLDescFromCFURL は Core Foundation URL を Apple イベントディスクリプタ
レコードにエンコードして、ポインタをディスクリプタレコードに返す。
エラーが発生すると、NULL が返される */

AEDesc * FURLDescFromCFURL(AEDesc *furlDesc, CFURLRef url) {
CFDataRef theData;
OSStatus err;
AEDesc *furlResult;

/* ローカル変数を既知のステータスにセットアップする */
furlResult = NULL;

/* URL を UTF8 データストリングにエンコードする */
theData = CFURLCreateData(nil, url, kCFStringEncodingUTF8, true);
if (theData != NULL) {

/* データをディスクリプタに入力する */
err = AECreateDesc('furl', CFDataGetBytePtr(theData),
CFDataGetLength(theData), furlDesc);

/* 成功の場合は、結果をセットする */
if (err == noErr) {
furlResult = furlDesc;
}
/* ローカルバッファを解放する */
CFRelease(theData);
}
/* ポインタを furl ディスクリプタに返す */
return furlResult;
}

/* デコード -> CFURL
FURLDescToCFURLはfurlディスクリプタを含むApple イベントディスクリプタ
レコードをデコードし、Core Foundation URLを返す。
エラーが発生すると、NULLが返される */

CFURLRef FURLDescToCFURL(AEDesc *furlDesc) {
Ptr dataPtr;
Size bytecount;
CFURLRef url;
OSStatus err;

/* ローカル変数を既知のステータスにセットアップする */
url = NULL;

/* データタイプが正しいかを検証する */
if (furlDesc->descriptorType == 'furl') {

/* ディスクリプタ内のバイト数をカウントする */
bytecount = AEGetDescDataSize(furlDesc);

/* バイト数に応じてローカルバッファを割り当てる */
dataPtr = malloc(bytecount);
if (dataPtr != NULL) {

/* ディスクリプタからバイト数をコピーする */
err = AEGetDescData(furlDesc, dataPtr, bytecount);
if (err == noErr) {

/* Core Foundation URL を作成する */
url = CFURLCreateWithBytes(nil, dataPtr, bytecount,
kCFStringEncodingUTF8, nil);

}
/* ローカルバッファの割り当てを解放する */
free(dataPtr);
}
}
/* 新たな URL を返す */
return url;
}

リスト1 'furl' Apple イベントディスクリプタレコードのエンコード、およびデコードの方法を示すルーチン。このルーチンはデータタイプの機能的な定義を提供する



これらはアプリケーションがファイル位置をやり取りするために使用するランタイムデータタイプです。このタイプのバイナリフォーマットの定義はこのテクニカルノートには用意されていません。このデータタイプに使われたバイナリエンコーディングの詳細については、Core Foundation の技術文書をお読みください。

Back to top

typeFileURL はいつ使うか

typeFileURL は、未作成ファイルへの参照を可能にするため特別に設計されました。その主な機能はアプリケーション間、あるいはアプリケーション内でやりとりされる "Save As..." スタイルの Apple イベント内で、ファイルの命名と参照を可能にすることです。

ユーザインタフェースコマンドに応じた処理をするために、すでに要素化されていて、自分自身に Apple イベントを送るタイプのアプリケーションでは、typeFileURL は必要なエンコーディングメカニズムです。従来のエンコーディングスキーマでは、Mac OS X で未作成ファイルへの参照をエンコードすることはできません。



図1 Apple イベントでファイルへの参照をエンコードするために typeFileURL フォーマットの使用方法を示す。
注意:要素アプリケーションでは、送信側アプリケーションも受信側アプリケーションも同一のものとなります。



Back to top

Navigation Service の応答から typeFileURL を作成する

図 1 で示したプロセスの第 1 段階は、未作成ファイルを参照する Navigation Service の応答から Core Foundation URL を取得することになります。Mac OS X では、NavCreatePutFileDialog/NavDialogRun シーケンスによって返されたNavigation Service の応答レコードは、新しくファイルが作成されるフォルダを参照している FSRef を含みます。そのファイルは、NavReplyRecord の Selection フィールドにある AEDescList の最初の AEDesc レコードに作成されます。新しいファイルの名前は saveFileName フィールドによって参照された CFString に含まれることになります。リスト 2 は、その作成例のひとつです。



/* GetCFURLFromNavReplyはNavCreatePutFileDialog/NavDialogRun
シーケンスに応じて、未作成ファイルを参照する URL を返す */
CFURLRef GetCFURLFromNavReply(const NavReplyRecord * navReply) {
OSStatus err;
FSRef parentFSRef;
CFURLRef parentURLRef, fullURLRef;
AEKeyword theAEKeyword;
DescType typeCode;
Size actualSize;

/* ローカル変数が既知のステータスにあることを確認する */
fullURLRef = NULL;

/* 親ディレクトリを参照している FSRef を取得する */
err = AEGetNthPtr(&navReply->selection, 1, typeFSRef,
&theAEKeyword, &typeCode, &parentFSRef, sizeof(FSRef), &actualSize);
if (err == noErr) {

/* FSRef を Core Foundation URL に変換する */
parentURLRef = CFURLCreateFromFSRef(NULL, &parentFSRef);
if (parentURLRef != NULL) {

/* URL の最後にファイル名を加える */
fullURLRef = CFURLCreateCopyAppendingPathComponent(NULL,
parentURLRef, navReply->saveFileName, false);

/* 親 URL へのパスを解放する */
CFRelease(parentURLRef);
}
}
/* 新たな URL に参照を返す */
return fullURLRef;
}

リスト2 NavCreatePutFileDialog/NavDialogRun シーケンスによって返された Navigation Service の応答レコードを使った Core Foundation URL の作成例



一度ファイル参照が CFURL としてエンコードされると、FURLDescFromCFURL のようなルーチン (リスト 1 参照)は、'furl' フォーマットされたディスクリプタとして Apple イベントディスクリプタレコードにエンコードするために使うことができます。このフォーマットへの変換が終われば、Apple イベントへの転送準備ができたことになります。

Back to top

typeFileURL によって参照されるファイルを作成する

typeFileURL を含む Apple イベントディスクリプタレコードをデコードするには、リスト 1 に示した FURLDescToCFURL ルーチンと同じようなテクニックを使う必要があります。これで未作成ファイルを参照する CFURL を提供できますし、ディレクトリ(ファイルが作られることになる)への参照を生成するとともに、これから作るファイルの命名もできます。リスト 3 に示すように、これらの項目はファイル作成操作を実行するために、続いて FSCreateFileUnicode に渡されます。ファイルが作成されてしまえば、FSRef レコードを使って参照することができます



/* CreateFileUsingCFURL は、提供されたカタログパラメータを使って、
CFURL が参照したファイルを作成する */
OSStatus CreateFileUsingCFURL(
FSRef *newFileReference,
CFURLRef url,
FSCatalogInfoBitmap whichInfo,
const FSCatalogInfo * catalogInfo) {

CFURLRef parentURL;
CFStringRef fileNameRef;
FSRef parentDirectory;
UniCharPtr nameStringPtr;
OSStatus err;

/* ローカル変数を既知のステータスにセットする */
err = coreFoundationUnknownErr;

/* 親ディレクトリへの URL を取得する */
parentURL = CFURLCreateCopyDeletingLastPathComponent(NULL, url);
if (parentURL != NULL) {

/* URL を FSRef に変換する */
if (CFURLGetFSRef(parentURL, &parentDirectory)) {

/* URL からリーフ名を取得する */
fileNameRef = CFURLCopyLastPathComponent(url);
if (fileNameRef != NULL) {

/* URL からリーフ名を取得する */
nameStringPtr = CFStringGetCharactersPtr(fileNameRef);
if (nameStringPtr != NULL) {

/* ファイルを作成する */
err = FSCreateFileUnicode(
&parentDirectory,
CFStringGetLength(fileNameRef),
nameStringPtr,
whichInfo, catalogInfo,
newFileReference, NULL);
}
/* ファイル名を解放する */
CFRelease(fileNameRef);
}
}
/* 親 URL を解放する */
CFRelease(parentURL);
}
/* ステータス値を返す */
return err;
}

リスト3 FSCreateFileUnicode ルーチンを使った、CFURL が参照するファイルの作成例



Back to top

typeFileURL を使った上級ルーチン

このテクニカルノートで示したサンプルコードは解説を目的として選んだもので、考え方を理解していただくために書かれています。上級ユーザは、後述のダウンロードファイル内のサンプルコードリストを参照してください。このソースファイルには、以下の例が含まれています。
  • typeFileURL typeChar、typeStyledText、typeUnicodeText、cFile、typeFSS、typeFSRef、typeAlias とtypeObjectSpecifier に関連した自動変換用の Apple イベント強制ハンドラの実装。
  • Apple イベントレコード内で typeFileURL を使うユーティリティルーチン。

Back to top

ダウンロード

Bluebook gif

サンプルコード(12K)

ダウンロード


Back to top